#include <sys/socket.h>	  /* for socket(), connect(), send(), and recv() */
#include <stdlib.h>	  /* supports all sorts of functionality */
#include <string.h>

#include "common.h"

/**
 * Initializes a ReadData structure for a socket.
 */
ReadData read_init(int socket)
{
	/* Don't need to memset the whole thing as long as dataLocation == bufferSize. */

	ReadData rd;
	rd.socket = socket;
	rd.dataLocation = rd.bufferSize;

	return rd;
}

/**
 * Fills the data buffer from the socket.
 * Returns nonzero on success.
 */
int receive_data(ReadData *rd)
{
	rd->dataLocation = 0;
	rd->bufferSize = recv(rd->socket, rd->buffer, BUFSIZE, 0);
	return rd->bufferSize > 0;
}

/**
 * Reads a byte from the data into the location pointed to by c.
 * Returns nonzero on success.
 */
int read_byte(ReadData *rd, char *c)
{
	if (rd->dataLocation >= rd->bufferSize)
	{
		if (!receive_data(rd))
		{
			return 0;
		}
	}

	*c = rd->buffer[rd->dataLocation++];

	return -1;
}

/**
 * Reads dataLen bytes into the location pointed to by data.
 * Returns nonzero on success.
 */
int read_data(ReadData *rd, void *data, size_t dataLen)
{
	size_t read = 0;

	while (read < dataLen)
	{
		ssize_t available = rd->bufferSize - rd->dataLocation;

		if (available > 0)
		{
			if (read + available >= dataLen)
			{
				available = dataLen - read;
			}

			memcpy(data + read, rd->buffer + rd->dataLocation, available);
			read += available;
		}

		if (read < dataLen)
		{
			if (!receive_data(rd))
			{
				return 0;
			}
		}
		else
		{
			rd->dataLocation += available;
		}
	}

	return -1;
}

/**
 * Reads characters into the location pointed to by str until
 * a newline or tab character is reached, or dataLen characters have been
 * read. If dataLen is reached, the buffer is flushed until it reaches a
 * tab or newline. A null will not be written into str, but the delimiting
 * tab or newline will.
 *
 * Returns the number of characters read, not including the delimiter,
 * dataLen on truncation (str is full), or -1 on error.
 */
int read_string(ReadData *rd, char *str, size_t dataLen)
{
	int x;
	char c;

	for (x = 0; x < dataLen; ++x)
	{
		if (!read_byte(rd, &c))
		{
			return -1;
		}

		str[x] = c;

		if (c == '\t' || c == '\n')
		{
			return x;
		}
	}

	/* Buffer is full, flush to the delimiter. */

	do
	{
		if (!read_byte(rd, &c))
		{
			return -1;
		}
	}
	while (c != '\t' && c != '\n');

	str[x - 1] = c;
	return dataLen;
}
